﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Web;
using System.IO;

namespace SBS.Common
{
    public class LogEntity
    {
        private static string GetLogFolder()
        {
            return SBS.Base.GlobalCache.GetSiteConfig().SelectSingleNode("/root/log").InnerText;
        }
        public string LogName;
        public DateTime DateCreated;
        public bool IsXml;
        public Log.Frequency Freq;
        private XmlDocument LogIndex;
        public LogEntity(string LogName)
        {
            this.LogName = LogName;
            XmlDocument dom = new XmlDocument();
            dom.Load(GetLogFolder()+"index.xml");
            this.LogIndex = dom;
            XmlNode LogNode = dom.SelectSingleNode("/root/log[@name='"+this.LogName+"']");
            if(LogNode==null) return;

            this.DateCreated = SBS.Common.Utility.GetDateTimeByString(LogNode.Attributes["datecreated"].Value);
            this.IsXml = LogNode.Attributes["isxml"].Value == "1";
            int f = 0;
            int.TryParse(LogNode.Attributes["freq"].Value, out f);
            this.Freq = (Log.Frequency)f;
        }
        public string LastLogString
        {
            get 
            {
                XmlNode item = this.LogIndex.SelectSingleNode("/root/log[@name='" + this.LogName + "']/item[position()=last()]");
                if (item == null) return null;
                if (this.IsXml == false)
                {
                    string txt = File.ReadAllText(GetLogFolder() + this.LogName + "\\" + item.InnerText + ".log");
                    return txt;
                }
                XmlReaderSettings Setting = new XmlReaderSettings();
                Setting.ProhibitDtd = false;
                XmlReader Reader = XmlReader.Create(GetLogFolder() + this.LogName + "\\" + item.InnerText + ".xml", Setting);
                string xmlcode = string.Empty;
                while (Reader.Read())
                {
                    if (Reader.NodeType == XmlNodeType.Element && Reader.Name == "root")
                    {
                        xmlcode = Reader.ReadOuterXml();
                        break;
                    }
                }
                Reader.Close();
                return xmlcode;
            }
        }
        public string LastLogFilePath
        {
            get 
            { 
                XmlNode item = this.LogIndex.SelectSingleNode("/root/log[@name='" + this.LogName + "']/item[position()=last()]");
                return GetLogFolder() + this.LogName + "\\" + item.InnerText + (this.IsXml?".xml":".log");
            }
        }
    }

    public class Log
    {
        //public static class Cycle
        //{
        //    public static string Daily
        //    {
        //        get
        //        {
        //            return System.DateTime.Now.ToString("yyyy-MM-dd");
        //        }
        //    }
        //    public static string Weekly
        //    {
        //        get
        //        {
        //            int week = System.Convert.ToInt32(System.DateTime.Now.DayOfWeek);
        //            DateTime sunday = System.DateTime.Now.AddDays(0 - week);
        //            return sunday.ToString("yyyy-MM-dd") + "--" + sunday.AddDays(6).ToString("yyyy-MM-dd");
        //        }
        //    }
        //    public static string Monthly
        //    {
        //        get
        //        {
        //            return System.DateTime.Now.ToString("yyyy-MM");
        //        }
        //    }
        //    public static string Yearly
        //    {
        //        get
        //        {
        //            return System.DateTime.Now.Year.ToString();
        //        }
        //    }
        //    public static string SetName(string LogName)
        //    {
        //        return LogName;
        //    }
        //}
        public enum Frequency
        {
            Daily = 0,
            Weekly = 1,
            Monthly = 2,
            Quarterly = 3,
            Yearly = 4
        }

        public static List<string> GetAllLogName()
        {
            XmlDocument dom = new XmlDocument();
            dom.Load(GetLogFolder() + "index.xml");
            XmlNodeList logs = dom.SelectNodes("/root/log/@name");
            List<string> Arr = new List<string>();
            foreach (XmlNode log in logs)
            {
                Arr.Add(log.Value);
            }
            return Arr;
        }

        public static LogEntity GetLogEntity(string LogName)
        {
            LogEntity LE = new LogEntity(LogName);
            return LE;
        }

        public class XmlLog
        {
            private string LogName = "";
            private string LogFolder = "";
            public Frequency Freq;
            public string Url = "";
            public string Referer = "";
            public string Cookies = "";
            public string UserAgent = "";
            public string Ip = "";
            public string AcceptLanguage = "";
            public string RequestMethod = "";
            public string ErrorMsg = "";
            public string Source = "";
            public string StackTrace = "";
            private bool init = false;

            public XmlLog(string LogName, Frequency Freq)
            {                                                               
                this.Freq = Freq;
                this.LogName = LogName;                
                this.LogFolder = Log.GetLogFolder() + LogName+ "\\";                
            }
            /// <summary>
            /// 收集当前 HttpContext.Current 对象的一些参数：RawUrl, UrlReferrer, Cookies, UserAgent, Ip, AcceptLanguage, RequestMethod 等
            /// </summary>
            public void InitHttpContext()
            {
                this.Url = HttpContext.Current.Request.RawUrl;
                if (HttpContext.Current.Request.UrlReferrer != null)
                {
                    this.Referer = HttpContext.Current.Request.UrlReferrer.ToString();
                }
                this.Cookies = HttpContext.Current.Request.Headers["Cookie"];
                if (HttpContext.Current.Request.UserAgent != null)
                {
                    this.UserAgent = HttpContext.Current.Request.UserAgent;
                }
                this.Ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
                this.AcceptLanguage = HttpContext.Current.Request.ServerVariables["HTTP_ACCEPT_LANGUAGE"];
                this.RequestMethod = HttpContext.Current.Request.HttpMethod;
                this.init = true;
            }

            public void Save()
            {
                
                if (!Directory.Exists(this.LogFolder))
                {
                    Directory.CreateDirectory(this.LogFolder);
                }
                string filename = GetFileNameByFrequency(this.Freq);

                if (!File.Exists(this.LogFolder + filename + ".xml"))
                {
                    StreamWriter init = new StreamWriter(this.LogFolder + filename + ".xml", false, Encoding.UTF8);
                    init.WriteLine("<?xml version='1.0' encoding='utf-8'?>");
                    init.WriteLine("<!DOCTYPE root [");
                    init.WriteLine("<!ENTITY xmlcode SYSTEM \""+filename+".txt\">");
                    init.WriteLine("]>");
                    init.WriteLine("<root>");
                    init.WriteLine("&xmlcode;");
                    init.WriteLine("</root>");
                    init.Close();

                    StreamWriter init2 = new StreamWriter(this.LogFolder + filename + ".txt", false, Encoding.UTF8);
                    init2.Close();
                }

                MemoryStream ms = new MemoryStream();
                XmlTextWriter writer = new XmlTextWriter(ms, Encoding.UTF8);
                writer.WriteStartElement("error");
                if (this.init == true)
                {
                    writer.WriteAttributeString("ip", this.Ip);
                    writer.WriteAttributeString("url", this.Url);
                    writer.WriteAttributeString("referer", this.Referer);
                    writer.WriteAttributeString("cookies", this.Cookies);
                    writer.WriteAttributeString("useragent", this.UserAgent);
                    writer.WriteAttributeString("lang", this.AcceptLanguage);
                    writer.WriteAttributeString("method", this.RequestMethod);                 
                }
                writer.WriteAttributeString("datetime", SBS.Common.Utility.GetNowTime());
                if (!string.IsNullOrEmpty(this.ErrorMsg))
                {
                    writer.WriteStartElement("message");
                    writer.WriteString(this.ErrorMsg);
                    writer.WriteEndElement();
                }
                if (!string.IsNullOrEmpty(this.Source))
                {
                    writer.WriteStartElement("source");
                    writer.WriteString(this.Source);
                    writer.WriteEndElement();
                }
                if (!string.IsNullOrEmpty(this.StackTrace))
                {
                    writer.WriteStartElement("stacktrace");
                    writer.WriteCData(this.StackTrace);
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
                writer.Close();
                ms.Flush();
                byte[] bytes = ms.ToArray();
                ms.Close();
                
                FileStream fs = new FileStream(this.LogFolder + filename + ".txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                fs.Write(bytes, 0, bytes.Length);
                fs.Close();

                WriteLogToIndex(LogName, Freq, true);

            }
        }

        public static void WriteXmlLog(string LogName, string Cycle, string Content, Hashtable Param)
        {
            XmlLog Xl = new XmlLog(LogName, ConvertCycleStringToFreq(Cycle));
            Xl.InitHttpContext();
            StringBuilder sb = new StringBuilder();
            sb.Append(Content);
            foreach (string Key in Param.Keys)
            {
                sb.AppendFormat("{0}:{1}\r\n", Key, Param[Key].ToString());
            }
            Xl.ErrorMsg = sb.ToString();
            Xl.Save();
        }

        private static Frequency ConvertCycleStringToFreq(string Cycle)
        {
            Frequency Freq;
            switch (Cycle.ToLower())
            {
                case "daily":
                    {
                        Freq = Frequency.Daily;
                    }
                    break;
                case "weekly":
                    {
                        Freq = Frequency.Weekly;
                    }
                    break;
                case "monthly":
                    {
                        Freq = Frequency.Monthly;
                    }
                    break;
                case "quarterly":
                    {
                        Freq = Frequency.Quarterly;
                    }
                    break;
                case "yearly":
                    {
                        Freq = Frequency.Yearly;
                    }
                    break;
                default:
                    {
                        Freq = Frequency.Weekly;
                    }
                    break;
            }
            return Freq;
        }


        public static void WriteTxtLog(string LogName, string Cycle, string Content, double MaxSizeInMb)
        {
            Frequency Freq = ConvertCycleStringToFreq(Cycle);            
            WriteTxtLog(LogName, Freq, Content, MaxSizeInMb);
        }
        public static void WriteTxtLog(string LogName, string Content)
        {
            WriteTxtLog(LogName, Frequency.Monthly , Content, MaxSizeInMb);
        }
        public static void WriteTxtLog(string LogName, string Cycle, string Content)
        {
            WriteTxtLog(LogName, Cycle, Content, MaxSizeInMb);
        }
        public static void WriteTxtLog(string LogName, Frequency Freq, string Content)
        {
            WriteTxtLog(LogName, Freq, Content, MaxSizeInMb);
        }
        public static void WriteTxtLog(string LogName, Frequency Freq, string Content, double MaxSizeInMb)
        {
            string logfolder = GetLogFolder();
            if (!Directory.Exists(logfolder + LogName))
            {
                Directory.CreateDirectory(logfolder + LogName);
            }
            string logfilename = GetFileNameByFrequency(Freq);
            if (File.Exists(logfolder + LogName + "\\" + logfilename + ".log"))
            {
                FileInfo logfile = new FileInfo(logfolder + LogName + "\\" + logfilename + ".log");
                if (logfile.Length > MaxSizeInMb * 1048576) //1048576 = 1Mb
                {
                    logfile.MoveTo(logfile.DirectoryName + "\\" + logfilename + "." + SBS.Common.Utility.GetNowTime("yyyyMMddHHmmssfff") + ".log");
                }
            }
            StreamWriter writer = new StreamWriter(logfolder + LogName + "\\" + logfilename + ".log", true, Encoding.UTF8);
            writer.WriteLine("");
            writer.WriteLine("---" + SBS.Common.Utility.GetNowTime() + "---");
            writer.WriteLine(Content);
            writer.Close();
            writer.Dispose();
            WriteLogToIndex(LogName, Freq, false);
        }

        

        public static int MaxSizeInMb
        {
            get 
            {
                return 100;
            }
        }

        public static string GetFileNameByFrequency(Frequency Freq)
        {
            string logfilename = string.Empty;
            switch (Freq)
            {
                case Frequency.Daily:
                    {
                        logfilename = System.DateTime.Now.ToString("yyyy-MM-dd");
                    }
                    break;
                case Frequency.Weekly:
                    {
                        int week = System.Convert.ToInt32(System.DateTime.Now.DayOfWeek);
                        DateTime sunday = System.DateTime.Now.AddDays(0 - week);
                        logfilename = sunday.ToString("yyyy-MM-dd") + "--" + sunday.AddDays(6).ToString("yyyy-MM-dd");
                    }
                    break;
                case Frequency.Monthly:
                    {
                        logfilename = System.DateTime.Now.ToString("yyyy-MM");
                    }
                    break;
                case Frequency.Quarterly:
                    {
                        int startmonth = System.Convert.ToInt32(System.Math.Floor((double)System.DateTime.Now.Month / 3) * 3 + 1);
                        int endmonth = startmonth + 2;

                        string year = System.DateTime.Now.Year.ToString();
                        logfilename = year + "-" + (startmonth < 10 ? "0" + startmonth.ToString() : startmonth.ToString()) + "--" + year + "-" + (endmonth < 10 ? "0" + endmonth.ToString() : endmonth.ToString());
                    }
                    break;
                case Frequency.Yearly:
                    {
                        logfilename = System.DateTime.Now.Year.ToString();
                    }
                    break;
                default:
                    {//weekly
                        int week = System.Convert.ToInt32(System.DateTime.Now.DayOfWeek);
                        DateTime sunday = System.DateTime.Now.AddDays(0 - week);
                        logfilename = sunday.ToString("yyyy-MM-dd") + "--" + sunday.AddDays(6).ToString("yyyy-MM-dd");
                    }
                    break;
            }
            return logfilename;
        }

        private static string GetLogFolder()
        {
            return SBS.Base.GlobalCache.GetSiteConfig().SelectSingleNode("/root/log").InnerText;
        }

        private static void WriteLogToIndex(string LogName, Frequency Freq, bool IsXmlType)
        {
            if (!File.Exists(GetLogFolder() + "index.xml"))
            {
                File.WriteAllText(GetLogFolder() + "index.xml", "<?xml version='1.0' encoding='utf-8'?><root/>", Encoding.UTF8);                
            }
            XmlDocument dom = new XmlDocument();
            dom.Load(GetLogFolder() + "index.xml");
            bool Edit = false;
            if (dom.SelectSingleNode("/root/log[@name='" + LogName + "']") == null)
            {
                XmlElement Log = dom.CreateElement("log");
                Log.SetAttribute("name", LogName);
                Log.SetAttribute("isxml", IsXmlType == true ? "1" : "0");
                Log.SetAttribute("freq", ((int)Freq).ToString());
                Log.SetAttribute("datecreated", SBS.Common.Utility.GetNowTime());
                dom.DocumentElement.AppendChild(Log);
                Edit = true;
            }
            string FileName = GetFileNameByFrequency(Freq);
            if (dom.SelectSingleNode("/root/log[@name='" + LogName + "']/item[.='" + FileName + "']") == null)
            {
                XmlElement item = dom.CreateElement("item");
                item.InnerText = FileName;
                dom.SelectSingleNode("/root/log[@name='" + LogName + "']").AppendChild(item);                
                Edit = true;
            }
            if (Edit == true)
            {
                dom.Save(GetLogFolder() + "index.xml");
            }
        }

        public static string GetLogFilePath(string LogName, string Cycle)
        {
            LogEntity Le = new LogEntity(LogName);            
            Le.Freq = ConvertCycleStringToFreq(Cycle);
            return Le.LastLogFilePath;
        }

    }


}
